#pragma once
/** MTBLib::ShuffleStack by Medi H.B.T.
 * This is a header-only library. */

#include <string>
#include <random>
#include <exception>
#include <tuple>

namespace MTBLib {
    class ShuffleStack {
    public: ///< typedefs
        /** @brief 错误代码 */
        enum ErrorType {
            NO_ERR = 0x0,
            BASE     = 0x80020000, ///< ShuffleStack的基准错误码
            EMPTY    = 0x80020001, ///< 列表为空(没有未抽取的元素)
            FULL     = 0x80020002, ///< 列表已满
            OVERFLOW = 0x80020003, ///< 输入的值溢出
        };
        /** @brief 错误类型 */
        template <typename InfoType>
        struct Error {
            ErrorType err_code;
            InfoType  info;
            std::string error_str;
        };

    private:
        ///< fields
        int *id_buff, length;    ///< id列表
        int *rand_stack; ///< 存放取得的随机下标的栈
        int unshuffled_top;         ///< 未被抽取的栈顶
        int rand_top;               ///< 随机下标栈的栈顶
        std::random_device rand;    ///< 伪随机数生成器(PRNG)
        std::mt19937 rand_mt_gen;

    public:
        /** @fn ShuffleStack.ShuffleStack(int)
         * @brief 构造函数: 创建一个有固定长度的顺序打乱列表
         * @param elem_num 元素数量 */
        ShuffleStack(int elem_num)
            : rand(), rand_mt_gen(rand()){
            rand_top   = 0;
            unshuffled_top = length = elem_num;
            id_buff    = new int[elem_num];
            rand_stack = new int[elem_num];
            for (int i = 0; i < elem_num; i++)
                id_buff[i] = i;
        }
        ~ShuffleStack() {
            delete []id_buff;
            delete []rand_stack;
            unshuffled_top = length = 0;
        }


        ///< Methods
        /** 列表的基本属性 */
        bool empty() { return (unshuffled_top == 0); }
        bool full()  { return (unshuffled_top == length);}
        int extracted_length() { return rand_top; }
        int extracted_at(int index)
        {
            if (index < 0 || index >= rand_top) {
                throw Error<std::tuple<int, int>> {
                    ErrorType::OVERFLOW,
                    std::make_tuple(index, rand_top),
                    "Overflow: {index, rand_top}"
                };
            }

        }
    };
};
